﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using RecommenderSystem.misc;
//using log4net;


namespace RecommenderSystem.algorithms
{

    public abstract class Algoritm
    {
//        protected static ILog logger = log4net.LogManager.GetLogger(typeof(MemoryBased));


        private Dictionary<string, IEnumerable<string>> _recommendationList;
        protected ICollection<IDataRecord> _dataset;
        protected IDictionary<string, User> _users;

        public Algoritm(Dictionary<string, User> users, ICollection<IDataRecord> dataset)
        {
            _users = users;
            _dataset = dataset;
            this._recommendationList = new Dictionary<string, IEnumerable<string>>();
        }

        public abstract double predictRating(string userID, string itemID);
        public abstract double calculateSimilarity(User user, User otherUser);

        public virtual List<string> Recommend(string userID, int recommnedtationsNum)
        {

            if (!_recommendationList.ContainsKey(userID))
            {
                _recommendationList.Add(userID, Recommend(userID));
            }

            return _recommendationList[userID].Take(recommnedtationsNum).ToList();
        }


        protected IEnumerable<User> getKNearestNeighboors(string userId, int nearestNeighboors)
        {
            if (_users == null || !_users.ContainsKey(userId))
            {
                return new List<User>();
            }
            User user = _users[userId];
            List<User> userEnvironment = new List<User>(_users.Values.Where(x => !x.getUserId().Equals(user.getUserId())));
            if (nearestNeighboors < 0)
            {
                return userEnvironment;
            }
            Dictionary<User, double> usersSimilarity = new Dictionary<User, double>();

            foreach (User otherUser in userEnvironment)
            {
                usersSimilarity.Add(otherUser, calculateSimilarity(user, otherUser));
            }
            return usersSimilarity.OrderByDescending(x => x.Value).Select(x => x.Key).Take(nearestNeighboors);
        }


        private IEnumerable<string> Recommend(string userID)
        {
            List<string> result = new List<string>();
            if (!_users.ContainsKey(userID) )
            {
//                logger.Warn("There is no such user in data set: " + userID);
                return result;
            }
            //Retrieve from DB all the items that the UserID not rated. 
            var allItemsIds = (from record in _dataset
                               select record.getItemID())
                                  .Distinct();

            var userNotRatedItems = allItemsIds.Except(_users[userID].getRatedItemsIds());
            Dictionary<string, double> potentialItems = new Dictionary<string, double>();
            
            SortedSet<KeyValuePair<string, double>> sorted = new SortedSet<KeyValuePair<string, double>>(new Comparator());

            foreach (String itemId in userNotRatedItems)
            {
                double predictionRate = predictRating(userID, itemId);
                //potentialItems.Add(itemId, predictionRate);

                sorted.Add(new KeyValuePair<string,double>(itemId, predictionRate));
            }
            return sorted.Select(x => x.Key);
           // return potentialItems.OrderByDescending(x => x.Value).Select(x => x.Key);
        }

    }
 
}
